package edu.snake.implement.model.arena;


import edu.snake.core.model.Arena;
import edu.snake.core.model.Character;
import edu.snake.core.model.Character.CharacterName;
import edu.snake.core.model.Position;

import javax.swing.event.EventListenerList;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventListener;

public class ArenaImpl implements Arena {
    private volatile int width;
    private volatile int height;
    private volatile Character[][] grid;
    protected EventListenerList listenerList = new EventListenerList();
    private String actionCommand;

    public ArenaImpl(int width, int height) {
        this.width = width;
        this.height = height;
        grid = new Character[width][height];
        actionCommand = "Arena";
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }


    @Override
    public synchronized void setToArena(Position position, Character character) {
        final int width = position.getWidth();
        final int height = position.getHeight();


        checkCoordinate(width, height);
        grid[width][height] = character;


        CharacterName characterName;
        if (character != null) {
            characterName = character.getName();
        } else {
            characterName = CharacterName.EMPTY;
        }

        fireActionPerformed(new ActionEvent(this, 0, characterName.toString(),
                System.currentTimeMillis(),
                0));

    }

    @Override
    public synchronized Character getFromArena(Position position) {
        final int width = position.getWidth();
        final int height = position.getHeight();
        checkCoordinate(width, height);
        return grid[width][height];
    }

    private synchronized void checkCoordinate(int width, int height) {
        if (width >= grid.length || width < 0) {
            throw new OutOfAxisXGridException("X not exist " + width);
        }

        if (height >= grid[0].length || height < 0) {
            throw new OutOfAxixYGridException("Y not exist" + height);
        }
    }

    @Override
    public boolean checkFreeSpace() {
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (grid[i][j] == null) {
                    return true;
                }
            }
        }
        return false;
    }

    //region manage ActionListener

    /**
     * Adds an action listener to the <code>Arena</code>.
     *
     * @param listener the listener to add
     * @see #ArenaImpl
     */
    public void addActionListener(ActionListener listener) {
        listenerList.add(ActionListener.class, listener);
    }


    /**
     * Removes the specified action listener from the <code>ArenaImpl</code>.
     *
     * @param listener the listener to remove
     */
    public void removeActionListener(ActionListener listener) {
        listenerList.remove(ActionListener.class, listener);
    }


    /**
     * Returns an array of all the action listeners registered
     * on this timer.
     *
     * @return all of the timer's <code>ActionListener</code>s or an empty
     * array if no action listeners are currently registered
     * @see #addActionListener
     * @see #removeActionListener
     * @since 1.4
     */
    public ActionListener[] getActionListeners() {
        return listenerList.getListeners(ActionListener.class);
    }

    /**
     * Notifies all listeners that have registered interest for
     * notification on this event type.
     *
     * @param e the action event to fire
     * @see EventListenerList
     */
    protected void fireActionPerformed(ActionEvent e) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();

        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == ActionListener.class) {
                ((ActionListener) listeners[i + 1]).actionPerformed(e);
            }
        }
    }

    /**
     * Returns an array of all the objects currently registered as
     * <code><em>Foo</em>Listener</code>s
     * upon this <code>ArenaImpl</code>.
     * <code><em>Foo</em>Listener</code>s
     * are registered using the <code>add<em>Foo</em>Listener</code> method.
     * <p/>
     * You can specify the <code>listenerType</code> argument
     * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
     * For example, you can query a <code>ArenaImpl</code>
     * instance <code>t</code>
     * for its action listeners
     * with the following code:
     * <p/>
     * <pre>ActionListener[] als = (ActionListener[])(t.getListeners(ActionListener.class));</pre>
     * <p/>
     * If no such listeners exist,
     * this method returns an empty array.
     *
     * @param listenerType the type of listeners requested;
     *                     this parameter should specify an interface
     *                     that descends from <code>java.util.EventListener</code>
     * @return an array of all objects registered as
     * <code><em>Foo</em>Listener</code>s
     * on this timer,
     * or an empty array if no such
     * listeners have been added
     * @throws ClassCastException if <code>listenerType</code> doesn't
     *                            specify a class or interface that implements
     *                            <code>java.util.EventListener</code>
     * @see #getActionListeners
     * @see #addActionListener
     * @see #removeActionListener
     * @since 1.3
     */
    public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
        return listenerList.getListeners(listenerType);
    }

    /**
     * Returns the string that will be delivered as the action command
     * in <code>ActionEvent</code>s fired by this timer. May be
     * <code>null</code>, which is also the default.
     *
     * @return the action command used in firing events
     * @since 1.6
     */
    public String getActionCommand() {
        return actionCommand;
    }
    //endregion
}
